/**
 *@file dataStruct.h
 *@author lynxi
 *@version v1.0
 *@date 2023-03-07
 *@par Copyright:
 *© 2022 北京灵汐科技有限公司 版权所有。
 * 注意：以下内容均为北京灵汐科技有限公司原创，未经本公司允许，不得转载，否则将视为侵权；对于不遵守此声明或者其他违法使用以下内容者，本公司依法保留追究权。\n
 *© 2022 Lynxi Technologies Co., Ltd. All rights reserved.
 * NOTICE: All information contained here is, and remains the property of Lynxi.
 *This file can not be copied or distributed without the permission of Lynxi
 *Technologies Co., Ltd.
 *@brief 车牌检测用到的结构体
 */

#ifndef DATA_STRUCT_H
#define DATA_STRUCT_H
#include <lyn_api.h>
#include <lyn_ipe.h>
#include <lyn_plugin.h>
#include <lyn_stream.h>
#include <sys/time.h>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include "batchMem.hpp"
#include "bufferPool.hpp"
#include "framePool.hpp"
#include "frameRater.h"
#include "ipeParam.h"
#include "util.hpp"
#include "yolox_post_process.h"

struct Rect {
   private:
    int32_t _ensure_even(int32_t n) {
        if (n % 2 != 0) {
            return n + 1;
        }
        return n;
    }

   public:
    int32_t x, y, w, h;
    void show_attr() {
        std::cout << "x: " << x << " y: " << y << " w: " << w << " h: " << h
                  << std::endl;
    }
    void make_even() {
        x = _ensure_even(x);
        y = _ensure_even(y);
        w = _ensure_even(w);
        h = _ensure_even(h);
    }

    void make_within_boundary(int32_t boundary_w, int32_t boundary_h) {
        x = std::max(x, (int32_t)0);
        y = std::max(y, (int32_t)0);
        x = std::min(x, boundary_w - 8 - 1);  // min size is 8
        y = std::min(y, boundary_h - 8 - 1);
        w = std::min(w, boundary_w - x - 1);
        h = std::min(h, boundary_h - y - 1);
    }

    void make_size_valid() {
        w = std::max(w, (int32_t)8);
        h = std::max(h, (int32_t)8);
    }

    bool is_within_boundary(int32_t boundary_w, int32_t boundary_h) {
        return x >= 0 && y >= 8 && x <= boundary_w - 8 && y <= boundary_h - 8;
    }

    bool is_size_valid() { return w >= 8 && h >= 8; }
};

Rect get_rect(const BboxResult &r) {
    Rect rect;
    rect.x = r.xmin;
    rect.y = r.ymin;
    rect.w = r.xmax - r.xmin;
    rect.h = r.ymax - r.ymin;
    return rect;
}

Rect get_rect(const BboxResult &r, int width, int height) {
    Rect rect = get_rect(r);
    rect.make_within_boundary(width, height);
    rect.make_even();
    return rect;
}

Rect get_rect(const lynBox &r, int width, int height) {
    BboxResult br;
    br.xmax = r.xmax;
    br.xmin = r.xmin;
    br.ymax = r.ymax;
    br.ymin = r.ymin;
    Rect rect = get_rect(br);
    rect.make_within_boundary(width, height);
    rect.make_even();
    return rect;
}

class IpeParamPlateRecognition : IpeParamBase {
   public:
    IpeParamPlateRecognition(int iModelWidth, int iModelHeight)
        : IpeParamBase(iModelWidth, iModelHeight) {}
    ~IpeParamPlateRecognition() {}

    /**
     * @brief yolov5 IPE 处理
     *
     * @param  stream IPE 操作插入的流
     * @param  inputData 待处理的数据
     * @param  outputData IPE 处理之后的数据
     */
    void CalcParam(lynStream_t stream, void *inputData,
                   void *outputData) override {
        // 结构体reset
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescIn));
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescOut));
        CHECK_ERR(lynIpeResetConfigDesc(m_oConfigDesc));

        CHECK_ERR(lynIpeSetInputPicDesc(m_oPicDescIn, inputData, m_iImgWidth,
                                        m_iImgHeight, m_format));
        CHECK_ERR(lynIpeSetOutputPicData(m_oPicDescOut, outputData));
        CHECK_ERR(lynIpeSetResizeConfig(m_oConfigDesc, m_iResizeWidth,
                                        m_iResizeHeight));
        CHECK_ERR(lynIpeSetPadConfig(m_oConfigDesc, m_iPadY, m_iPadX, m_iPadY,
                                     m_iPadX, 114, 114, 114));
        CHECK_ERR(lynIpeSetC2CConfig(m_oConfigDesc, LYN_PIX_FMT_RGB24, 0));
        CHECK_ERR(lynIpeCalOutputPicDesc(m_oPicDescOut, m_oPicDescIn,
                                         m_oConfigDesc, 0));
        CHECK_ERR(lynIpeProcessAsync(stream, m_oPicDescIn, m_oPicDescOut,
                                     m_oConfigDesc));
    }

    void CalcParam(lynStream_t stream, Rect box, void *inputData,
                   void *outputData) {
        // 结构体reset
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescIn));
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescOut));
        CHECK_ERR(lynIpeResetConfigDesc(m_oConfigDesc));
        CHECK_ERR(lynIpeSetInputPicDesc(m_oPicDescIn, inputData, m_iImgWidth,
                                        m_iImgHeight, m_format));
        CHECK_ERR(lynIpeSetOutputPicData(m_oPicDescOut, outputData));
        CHECK_ERR(
            lynIpeSetCropConfig(m_oConfigDesc, box.x, box.y, box.w, box.h));
        CHECK_ERR(lynIpeSetResizeConfig(m_oConfigDesc, m_iModelWidth,
                                        m_iModelHeight));
        CHECK_ERR(lynIpeSetC2CConfig(m_oConfigDesc, LYN_PIX_FMT_RGB24, 0));
        CHECK_ERR(lynIpeCalOutputPicDesc(m_oPicDescOut, m_oPicDescIn,
                                         m_oConfigDesc, 0));
        CHECK_ERR(lynIpeProcess(m_oPicDescIn, m_oPicDescOut, m_oConfigDesc));
    }
    void *get_out_desc() { return m_oPicDescOut; }
    void SetImgInfo(int32_t width, int32_t height,
                    lynPixelFormat_t format) override {
        if (width > height) {
            m_iResizeWidth = m_iModelWidth;
            // & ~1 是为了取偶数，yuv格式不能用奇数去进行ipe
            m_iResizeHeight = (height * m_iModelWidth / width) & ~1;
            m_iPadX = 0;
            m_iPadY = (m_iModelHeight - m_iResizeHeight) / 2;  //
            if (m_iPadY < 0) {
                m_iResizeHeight = m_iModelHeight;
                m_iResizeWidth = (m_iModelHeight * width / height) & ~1;
                m_iPadX = (m_iModelWidth - m_iResizeWidth) / 2;
                m_iPadY = 0;
                if (m_iPadX + m_iPadX + m_iResizeWidth != m_iModelWidth) {
                    padx = m_iModelWidth - (m_iPadX + m_iPadX + m_iResizeWidth);
                }
            }
        } else {
            m_iResizeWidth = (width * m_iModelWidth / height) & ~1;
            m_iResizeHeight = m_iModelHeight;
            m_iPadX = (m_iModelWidth - m_iResizeWidth) / 2;
            m_iPadY = 0;
        }
        IpeParamBase::SetImgInfo(width, height, format);
    }

   private:
    // 图片resize参数
    int m_iResizeWidth = 0;
    int m_iResizeHeight = 0;
    // 图片填充参数
    int m_iPadX = 0;
    int m_iPadY = 0;
    int padx = 0;
};

enum IPEType { Crop, Resize, Pad, C2C };

struct IpeInfo {
    int32_t cropX;
    int32_t cropY;
    int32_t cropWidth;
    int32_t cropHeight;

    // 图片resize参数
    int32_t resizeWidth;
    int32_t resizeHeight;

    // 图片填充参数
    int32_t padTop;
    int32_t padRight;
    int32_t padBottom;
    int32_t padLeft;

    lynPixelFormat_t c2cFormat;
};

class IpeParamModel : IpeParamBase {
   public:
    IpeParamModel(int iModelWidth, int iModelHeight)
        : IpeParamBase(iModelWidth, iModelHeight) {}
    ~IpeParamModel() {}

    void SetModelType(ModelType modelType) { m_modeltype = modelType; }

    /**
     * @brief  IPE 处理
     *
     * @param  stream IPE 操作插入的流
     * @param  inputData 待处理的数据
     * @param  outputData IPE 处理之后的数据
     */
    virtual void CalcParam(lynStream_t stream, void *inputData,
                           void *outputData) {
        // 结构体reset
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescIn));
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescOut));
        CHECK_ERR(lynIpeResetConfigDesc(m_oConfigDesc));
        CHECK_ERR(lynIpeSetInputPicDesc(m_oPicDescIn, inputData, m_iImgWidth,
                                        m_iImgHeight, m_format));

        // 设置操作
        for (auto operation : ipeProcess) {
            switch (operation) {
                case Crop:
                    CHECK_ERR(lynIpeSetCropConfig(
                        m_oConfigDesc, processInfo.cropX, processInfo.cropY,
                        processInfo.cropWidth, processInfo.cropHeight));
                    break;
                case Resize:
                    CHECK_ERR(lynIpeSetResizeConfig(m_oConfigDesc,
                                                    processInfo.resizeWidth,
                                                    processInfo.resizeHeight));
                    ;
                    break;
                case Pad:
                    CHECK_ERR(lynIpeSetPadConfig(
                        m_oConfigDesc, processInfo.padTop, processInfo.padRight,
                        processInfo.padBottom, processInfo.padLeft, 114, 114,
                        114));
                    break;
                case C2C:
                    CHECK_ERR(lynIpeSetC2CConfig(m_oConfigDesc,
                                                 processInfo.c2cFormat, 0));
                    break;
                default:
                    break;
            }
        }

        CHECK_ERR(lynIpeSetOutputPicData(m_oPicDescOut, outputData));
        CHECK_ERR(lynIpeCalOutputPicDesc(m_oPicDescOut, m_oPicDescIn,
                                         m_oConfigDesc, 0));
        CHECK_ERR(lynIpeProcessAsync(stream, m_oPicDescIn, m_oPicDescOut,
                                     m_oConfigDesc));
    }

    void SetImgInfo(int32_t width, int32_t height,
                    lynPixelFormat_t format) override {
        if (m_modeltype == Yolov5) {
            if (width > height) {
                processInfo.resizeWidth = m_iModelWidth;
                // & ~1 是为了取偶数，yuv格式不能用奇数去进行ipe
                processInfo.resizeHeight =
                    (height * m_iModelWidth / width) & ~1;
                processInfo.padRight = processInfo.padLeft = 0;
                processInfo.padTop = processInfo.padBottom =
                    (m_iModelHeight - processInfo.resizeHeight) / 2;  //
            } else {
                processInfo.resizeWidth = (width * m_iModelWidth / height) & ~1;
                processInfo.resizeHeight = m_iModelHeight;
                processInfo.padRight = processInfo.padLeft =
                    (m_iModelWidth - processInfo.resizeWidth) / 2;
                processInfo.padTop = processInfo.padBottom = 0;
            }
            processInfo.c2cFormat = LYN_PIX_FMT_RGB24;
            ipeProcess = {Resize, Pad, C2C};
        }
        IpeParamBase::SetImgInfo(width, height, format);
    }

    void MergeImage(lynStream_t stream, void *outputData,
                    std::vector<ImageMergeInfo> &inputDatas) {
        CHECK_ERR(lynIpeResetPicDesc(m_oPicDescOut));
        CHECK_ERR(lynIpeResetConfigDesc(m_oConfigDesc));
        CHECK_ERR(lynIpeSetOutputPicDesc(m_oPicDescOut, outputData, m_iImgWidth,
                                         m_iImgHeight, m_format));
        for (auto inputData : inputDatas) {
            if (inputData.frame == nullptr) {
                continue;
            }
            CHECK_ERR(lynIpeResetPicDesc(m_oPicDescIn));
            CHECK_ERR(lynIpeSetInputPicDesc(
                m_oPicDescIn, inputData.frame->data, inputData.videoInfo.width,
                inputData.videoInfo.height, m_format));
            CHECK_ERR(lynIpeSetCropConfig(m_oConfigDesc, 0, 0,
                                          inputData.videoInfo.width,
                                          inputData.videoInfo.height));
            CHECK_ERR(lynIpeSetOutputRect(m_oConfigDesc, inputData.x,
                                          inputData.y, inputData.width,
                                          inputData.heigh, false));
            CHECK_ERR(lynIpeProcessAsync(stream, m_oPicDescIn, m_oPicDescOut,
                                         m_oConfigDesc));
        }
    }

   private:
    ModelType m_modeltype;
    IpeInfo processInfo;
    std::vector<IPEType> ipeProcess;
};
#endif